/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.UDPGUI;

import java.awt.event.KeyEvent;
import java.io.IOException;
import java.net.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
import javax.swing.text.DefaultCaret;

/**
 *
 * @author 
 */
public class UDPGUI extends javax.swing.JFrame {

    /**
     * Creates new form UDPGUI
     */
    public UDPGUI() {
        initComponents();
    }
    SwingWorker Client;
    SwingWorker Server;
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    //the code in initComponents are autogenerated by the WYSIWYG editor in netbeans.
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jTextFieldMS = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        jButton1 = new javax.swing.JButton();
        jLabel3 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextAreaServer = new javax.swing.JTextArea();
        jLabel4 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        jTextAreaClient = new javax.swing.JTextArea();
        jLabel5 = new javax.swing.JLabel();
        stopButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jLabel1.setText("Timeout Period in milliseconds:");

        jTextFieldMS.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jTextFieldMSActionPerformed(evt);
            }
        });
        jTextFieldMS.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                jTextFieldMSKeyPressed(evt);
            }
        });

        jLabel2.setFont(new java.awt.Font("Tahoma", 0, 14)); // NOI18N
        jLabel2.setText("UDP Client");

        jButton1.setText("Run");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jLabel3.setText(" ");

        jScrollPane1.setAutoscrolls(true);

        jTextAreaServer.setEditable(false);
        jTextAreaServer.setColumns(20);
        jTextAreaServer.setRows(5);
        jScrollPane1.setViewportView(jTextAreaServer);

        jLabel4.setText("Server Output:");

        jScrollPane2.setAutoscrolls(true);

        jTextAreaClient.setEditable(false);
        jTextAreaClient.setColumns(20);
        jTextAreaClient.setRows(5);
        jScrollPane2.setViewportView(jTextAreaClient);

        jLabel5.setText("Client Output:");

        stopButton.setText("Stop");
        stopButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                stopButtonMouseClicked(evt);
            }
        });

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(244, 244, 244)
                        .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(jTextFieldMS, javax.swing.GroupLayout.PREFERRED_SIZE, 102, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(jButton1)
                        .addGap(18, 18, 18)
                        .addComponent(stopButton))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(18, 18, 18)
                        .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 440, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel4))
                        .addGap(171, 171, 171)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel5)
                            .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 440, javax.swing.GroupLayout.PREFERRED_SIZE))))
                .addContainerGap(42, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGap(5, 5, 5)
                .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jTextFieldMS, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jButton1)
                    .addComponent(stopButton))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel3)
                .addGap(28, 28, 28)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(3, 3, 3)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 429, Short.MAX_VALUE)
                    .addComponent(jScrollPane1))
                .addContainerGap(20, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(54, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jTextFieldMSActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jTextFieldMSActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_jTextFieldMSActionPerformed
    //this is an event listner waiting for the user to hit enter in the text field. 
    //Calls the jButton1Action to run the program
    private void jTextFieldMSKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jTextFieldMSKeyPressed
        // TODO add your handling code here:
        if(evt.getKeyCode()==KeyEvent.VK_ENTER)
        {
            java.awt.event.ActionEvent t = null;
            jButton1ActionPerformed(t);
        }
    }//GEN-LAST:event_jTextFieldMSKeyPressed
    //this is executed when the user clicks the start button
    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed

        jTextAreaServer.setText("");
        jTextAreaClient.setText("");
        boolean valid = true;
        int timeout = 0;//initialize the timeout
        try {
            timeout = Integer.parseInt(jTextFieldMS.getText()); //trys to get the value from the text field.
            if(timeout <= 0)
            {
                throw new Exception();
            }
        } catch (Exception e) { //if it fails then the input is not valid. 
            valid = false;
            //jLabel3.setText("Input not valid"); //notify the user.
            String st="Please enter a valid timeout value";
            JOptionPane.showMessageDialog(rootPane, st, null, JOptionPane.ERROR_MESSAGE); 
            //System.out.println("Not the right input");
        }
        if (valid) {//if the input is valid.
            jLabel3.setText("");
            /*
             * Swing worker server runs the server 
             */
            Server = new SwingWorker() {
                @Override
                protected Object doInBackground() throws Exception {
                    DatagramSocket serverSocket = null;
                    boolean running = true;
                    Stack<String> mailbox = new Stack<String>(); //create stack to compare the last message.
                    try {
                        serverSocket = new DatagramSocket(9999);
                    } catch (SocketException e) {
                        // TODO Auto-generated catch block
                        jTextAreaServer.append("Socket is already bounded.\n");
                        throw new UnsupportedOperationException("Socket already bounded.");
                    }

                    byte[] receiveData = new byte[1024];
                    byte[] sendData = new byte[1024];
                    jTextAreaServer.append("Server Started\n");
                    while (running) {
                        String lastMessage = null; //last message.
                        if (mailbox.size() > 0) {
                            lastMessage = mailbox.pop(); //if the stack isn't empty take the last value from stack for last message.
                        }
                        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                        try {
                            serverSocket.receive(receivePacket);
                        } catch (IOException ex) {
                            //Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        //get incoming message
                        String incoming = new String(receivePacket.getData());
                        //System.out.println(incoming);
                        //split the message by the deliminator #
                        String[] split = incoming.split("#");
                        String tc1 = split[0];
                        String seq = split[1];//sequence numbers.
                        int nextSeq = Integer.parseInt(seq) + 1;
                        String msg = split[2];
                        mailbox.push(msg); //push message on to stack
                        // System.out.println(split[0]);
                        //  System.out.println(split[1]);
                        //  System.out.println(split[2]);

                        DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss:SSSS");
                        Date date = new Date();
                        String ts = dateFormat.format(date);//time stamp

                        //System.out.println("RECEIVED: " + sentence);

                        InetAddress IPAddress = receivePacket.getAddress();
                        int port = receivePacket.getPort();

                        //String capitalizedSentence = sentence.toUpperCase();
                        //build packet to send to client
                        String packet = ts + "#" + tc1 + "#" + Integer.toString(nextSeq) +"#";
                        System.out.println(packet);
                        //sendData = capitalizedSentence.getBytes();
                        sendData = packet.getBytes();
                        DatagramPacket sendPacket =new DatagramPacket(sendData, sendData.length, IPAddress, port);
                        try {
                            serverSocket.send(sendPacket);
                        } catch (IOException ex) {
                            System.out.println("Error sending");
                        }
                        if (!msg.equals(lastMessage)) //if last message is the same as the msg coming in, dont' display.
                        {
                            //"server then prints the following"
                            /*System.out.println("tc1(from client) = " + split[0]);
                            System.out.println("ts(server time) = " + ts);
                            System.out.println("Sequence number(from client) = " + seq);
                            System.out.println("Message(from client) = " + msg);
                            System.out.println("========================================");*/
                            jTextAreaServer.append("tc1(from client) = " + split[0]+"\n");
                            jTextAreaServer.append("ts(server time) = " + ts+"\n");
                            jTextAreaServer.append("Sequence number(from client) = " + seq+"\n");
                            jTextAreaServer.append("Message(from client) = " + msg+"\n");
                            jTextAreaServer.append("================================================\n");
                            jTextAreaServer.setCaretPosition(jTextAreaServer.getDocument().getLength());

                        }
                        else
                        {
                            jTextAreaServer.append("DUPLICATE MESSAGE\n");
                            jTextAreaServer.append("================================================\n");
                            jTextAreaServer.setCaretPosition(jTextAreaServer.getDocument().getLength());
                        }
                    }

                    serverSocket.close();

                    throw new UnsupportedOperationException("Not supported yet.");
                }
            };//end of server worker
            
            //start of the client swing worker. 
            Client = new SwingWorker() {
                @Override
                protected Object doInBackground() throws Exception {
                    int time = Integer.parseInt(jTextFieldMS.getText());

                    boolean fail = false;
                    int count = 1;
                    String s = "0";//sequence numbers. starts at 0
                    //jLabel3.setText("Input not valid");
                    do {

                        int timeOutPeriod = time; //reset time out period.
                        //TODO: TIMEOUT PERIOD IS USER INPUT
                        DatagramSocket clientSocket = null;
                        try {
                            clientSocket = new DatagramSocket();
                        } catch (SocketException e) {
                        }
                        InetAddress IPAddress = null;
                        try {
                            IPAddress = InetAddress.getByName("localhost");
                        } catch (UnknownHostException ex) {
                        }
                        byte[] sendData = new byte[1024];
                        byte[] receiveData = new byte[1024];
                        DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss:SSSS");
                        Date date = new Date();
                        String tc1 = dateFormat.format(date);//time stamp

                        
                        String m = "One-Line Message M number:" + count++;
                        //build packet to send to server.
                        String buffer = tc1 + "#" + s + "#" + m + "#";
                        //System.out.println("This: "+buffer);
                        sendData = buffer.getBytes();
                        DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 9999);//buffer, length of buffer, destination ip, port
                        try {
                            clientSocket.send(sendPacket);
                        } catch (IOException ex) {
                        }

                        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                        try {
                            clientSocket.setSoTimeout(timeOutPeriod); //set timeout period
                        } catch (SocketException ex) {
                        }
                        int attempt = 0; //amount of resent/receive attempts.
                        fail = false; //if number of attempts is exceeded.
                        while (true) {

                            try {

                                //START DEBUG INFO> REMOVE IN FINAL
                                //System.out.println("Attempt: " + attempt);
                                jTextAreaClient.append("Attempt: " + (attempt+1) +"\n");
                                attempt++;

                                jTextAreaClient.append("Wait to Read\n");
                                try {
                                    
                                    clientSocket.receive(receivePacket);
                                } catch (IOException ex) {
                                    throw new SocketTimeoutException();//throw SocketTimeoutException
                                }

                                break; //if message is recieved break loop to continue
                            } catch (SocketTimeoutException e) {
                                // resend

                                if (attempt > 2)//to make sure it attempts a send and receive a total of a full 3 times.
                                {
                                    fail = true; //throw failed flag
                                    break; //if failed break to continue
                                }
                                //if haven't failed yet continue.
                                tc1 = dateFormat.format(date);//time stamp

                                //s = "0";//sequence numbers.
                                buffer = tc1 + "#" + s + "#" + m + "#";//resend same message with new  timestamp
                                sendData = buffer.getBytes();
                                sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, 9999);//buffer, length of buffer, destination ip, port
                                try {
                                    clientSocket.send(sendPacket);
                                } catch (IOException ex) {
                                    //Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
                                }
                                //set new timeout period.
                                timeOutPeriod *= 2;
                                try {
                                    clientSocket.setSoTimeout(timeOutPeriod);
                                } catch (SocketException ex) {
                                    //Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
                                }
                            }

                        }
                        if (fail) //if failed
                        {
                            jTextAreaClient.append("TIMED OUT\n");
                            jTextAreaClient.append("The program attempted to send/receive 3 times and failed....exiting\n");
                            jTextAreaClient.setCaretPosition(jTextAreaClient.getDocument().getLength());

                        } else //if not failed print out message from server.
                        {
                            date = new Date();
                            String tc2 = dateFormat.format(date);
                            String fromServer = new String(receivePacket.getData());
                            //System.out.println("FROM SERVER:" + modifiedSentence);
                            String[] split = fromServer.split("#");
                            //System.out.println(split[0]);
                            //System.out.println(split[1]);
                            //System.out.println(split[2]);

                            String ts = split[0];
                            tc1 = split[1];
                            String nextSeq = split[2];
                            System.out.println("'" + nextSeq +"'");
                            s=nextSeq;
                            //s=Integer.parseInt(nextSeq);
                            System.out.println("next seq to send is:" + s);
                            jTextAreaClient.append("tc2(local system time) = " + tc2+"\n");
                            jTextAreaClient.append("tc1(local system time message sent) = " + tc1+"\n");
                            jTextAreaClient.append("ts(server time message received = " + ts+"\n");
                            jTextAreaClient.append("Next sequence number = " + nextSeq+"\n");
                            jTextAreaClient.setCaretPosition(jTextAreaClient.getDocument().getLength());
                        }
                        clientSocket.close(); //close socket.
                    } while (true && fail == false); //exited if failed.
                    throw new UnsupportedOperationException("Not supported yet.");
                }
            };

                Server.execute(); //start the server
                Client.execute(); //start the client


        }
    }//GEN-LAST:event_jButton1ActionPerformed

    private void stopButtonMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_stopButtonMouseClicked
            Client.cancel(true);
    }//GEN-LAST:event_stopButtonMouseClicked
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;


                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(UDPGUI.class
                    .getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(UDPGUI.class
                    .getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(UDPGUI.class
                    .getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(UDPGUI.class
                    .getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>
        /* Create and display the form */
        //jScrollPane2 = new JScrollPane(jTextAreaClient); 
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new UDPGUI().setVisible(true);
                
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButton1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JTextArea jTextAreaClient;
    private javax.swing.JTextArea jTextAreaServer;
    private javax.swing.JTextField jTextFieldMS;
    private javax.swing.JButton stopButton;
    // End of variables declaration//GEN-END:variables
}
